#!/usr/bin/env python
# _*_ coding:utf-8 _*_

# Copyright (c) 2020 Huawei Technologies Co., Ltd.
# oec-hardware is licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of
# the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#     http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF
# ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
# FIT FOR A PARTICULAR
# PURPOSE.
# See the Mulan PSL v2 for more details.
# Create: 2020-11-03

""" the views of admin"""

import os
import json
import shutil
import base64
import datetime
from functools import wraps
from flask import render_template, redirect, url_for, flash, session, \
    request, abort
from sqlalchemy import or_

from app import db
from app.admin.forms import LoginForm, ManufacturerForm, ReviewForm, SearchForm
from app.models import Admin, User, Manufacturer, TestLog, TestReview, Hardware
from . import admin

dir_server = os.path.dirname(os.path.realpath(__file__))
dir_results = os.path.abspath(os.path.join(dir_server, "../../results"))
dir_files = os.path.join(dir_server, 'files')


def admin_login(func):
    """登录验证装饰器"""
    @wraps(func)
    def decorated_function(*args, **kwargs):
        if session.get('user'):
            return func(*args, **kwargs)
        return redirect(url_for('admin.login', next=request.path))
    return decorated_function


@admin.route('/')
@admin_login
def index():
    """管理员首页"""
    page = request.args.get('page', 1, type=int)  # 获取page参数值
    page_data = Manufacturer.query.order_by(
        Manufacturer.id.desc()
    ).paginate(page=page, per_page=10)
    return render_template("admin/manufacturer.html", page_data=page_data)


@admin.route('/manufacturer/add/', methods=["GET", "POST"])
@admin_login
def manufacturer_add():
    """
    添加厂商
    """
    if request.method == 'POST':
        manufacturer_name = request.form['manufacturer_name']
        uuid = request.form['uuid']
        manufacturer = Manufacturer.query.filter_by(
            manufacturer_name=manufacturer_name).count()
        if manufacturer:
            flash("厂商已存在", "err")
            return redirect(url_for("admin.manufacturer_add"))
        data = Manufacturer(
            manufacturer_name=manufacturer_name,
            uuid=uuid
        )
        db.session.add(data)
        db.session.commit()
        manu_path = os.path.join(dir_results, manufacturer_name)
        if not os.path.exists(manu_path):
            os.makedirs(manu_path)
        return redirect(url_for("admin.index"))
    return render_template("admin/manufacturer_add.html")


@admin.route("/manufacturer/edit/<int:manu_id>", methods=["GET", "POST"])
@admin_login
def manufacturer_edit(manu_id=None):
    """修改厂商"""
    manufacturer = Manufacturer.query.get_or_404(manu_id)
    form = ManufacturerForm()
    if request.method == "GET":
        form.manufacturer_name.data = manufacturer.manufacturer_name
        form.contract_no.data = manufacturer.uuid
    elif request.method == 'POST':
        manu_old_name = manufacturer.manufacturer_name
        manufacturer.manufacturer_name = request.form.get("manufacturer_name")
        manufacturer.uuid = request.form.get("contract_no")
        db.session.add(manufacturer)
        db.session.commit()

        manu_old_path = os.path.join(dir_results, manu_old_name)
        manu_new_path = os.path.join(dir_results, manufacturer.manufacturer_name)
        if os.path.exists(manu_old_path):
            os.rename(manu_old_path, manu_new_path)
        return redirect(url_for('admin.index'))

    return render_template("admin/manufacturer_edit.html", form=form)  # 渲染模板


@admin.route("/manu/del/<int:manu_id>", methods=["GET"])
@admin_login
def manufacturer_del(manu_id=None):
    """
    删除厂商
    """
    manu = Manufacturer.query.get_or_404(manu_id)  # 根据厂商id查找数据
    count = Hardware.query.filter_by(manu_id=manu_id).count()
    if count:
        return "<script>alert('厂商下面有硬件设备，请先删除硬件设备');" \
               "location.href='/admin/';</script>"
    db.session.delete(manu)
    db.session.commit()
    manu_path = os.path.join(dir_results, manu.manufacturer_name)
    if not os.listdir(manu_path):
        shutil.rmtree(manu_path)
    return redirect(url_for('admin.index', page=1))  # 渲染模板


@admin.route("/user/list/", methods=["GET"])
@admin_login
def user_list():
    """
    会员列表
    """
    page = request.args.get('page', 1, type=int)
    keyword = request.args.get('keyword', '', type=str)
    if keyword:
        filters = or_(User.username == keyword, User.email == keyword)
        page_data = User.query.filter(filters).order_by(
            User.id.asc()
        ).paginate(page=page, per_page=5)
    else:
        page_data = User.query.order_by(
            User.id.asc()
        ).paginate(page=page, per_page=5)

    return render_template("/admin/user_list.html", page_data=page_data)


@admin.route("/log/", methods=["GET", "POST"])
@admin_login
def log():
    """
    查询厂商列表
    """
    form = SearchForm()
    page = request.args.get('page', 1, type=int)
    if form.validate_on_submit():
        data = form.data
        page_data = Manufacturer.query.filter_by(
            manufacturer_name=data["search_creater"]).order_by(
            Manufacturer.id.desc()
        ).paginate(page=page, per_page=10)
    else:
        page_data = Manufacturer.query.order_by(
            Manufacturer.id.desc()
        ).paginate(page=page, per_page=10)
    return render_template("admin/log.html", page_data=page_data, form=form)


@admin.route('/log/<string:manu_id>/<string:hardware_id>/<string:taskid>',
             methods=['GET', 'POST'])
@admin_login
def get_logs(manu_id=None, hardware_id=None, taskid=None):
    """查看该task包含的所有日志"""
    form = SearchForm()
    page = request.args.get('page', 1, type=int)
    if form.validate_on_submit():
        data = form.data
        page_data = TestLog.query.filter_by(
            hardware_id=hardware_id).filter_by(
            job_name=data["search_creater"]).paginate(
            page=page, per_page=10)
    else:
        page_data = TestLog.query.filter_by(hardware_id=hardware_id).filter_by(
            task_id=taskid).order_by(TestLog.id.desc()).paginate(
            page=page, per_page=10)
    return render_template("admin/job_list.html", page_data=page_data,
                           form=form, manu_id=manu_id,
                           hardware_id=hardware_id, task_id=taskid)


@admin.route('/log/<string:manu_id>/<string:hardware_id>',
             methods=['GET', 'POST'])
@admin_login
def get_tasks(manu_id=None, hardware_id=None):
    """查看硬件设备下所有的task"""
    form = SearchForm()
    page = request.args.get('page', 1, type=int)
    if form.validate_on_submit():
        data = form.data
        page_data = TestLog.query.filter_by(
            hardware_id=hardware_id).filter_by(task_id=data["search_creater"])\
            .paginate(page=page, per_page=10)
    else:
        page_data = TestLog.query.filter_by(hardware_id=hardware_id).order_by(
            TestLog.id.desc()).paginate(page=page, per_page=10)
    return render_template("admin/task_lst.html", page_data=page_data,
                           form=form, manu_id=manu_id,
                           hardware_id=hardware_id)


@admin.route('/log/<string:manu_id>', methods=['GET', 'POST'])
@admin_login
def get_hardwares(manu_id=None):
    """查看厂商包含的硬件设备"""
    form = SearchForm()
    page = request.args.get('page', 1, type=int)
    if form.validate_on_submit():
        data = form.data
        page_data = Hardware.query.filter_by(
            manu_id=manu_id).filter_by(
            hardware=data["search_creater"]).paginate(page=page, per_page=10)
    else:
        page_data = Hardware.query.filter_by(manu_id=manu_id).order_by(
            Hardware.id.desc()).paginate(page=page, per_page=10)
    return render_template("admin/hardware_lst.html",
                           page_data=page_data, form=form, manu_id=manu_id)


@admin.route('/log/<string:manu>/<string:hardware>/<string:taskid>/'
             '<string:job_name>/', methods=['GET', 'POST'])
@admin_login
def get_job_detail(manu, hardware, taskid, job_name):
    """
    查询日志
    """
    manu_name = Manufacturer.query.filter_by(id=manu).first().manufacturer_name
    hardware_name = Hardware.query.filter_by(id=hardware).first().hardware
    dir_job = os.path.join(dir_results, manu_name,
                           hardware_name, taskid, job_name)
    form = ReviewForm()

    json_info = os.path.join(dir_job, 'compatibility.json')
    json_results = os.path.join(dir_job, 'factory.json')
    try:
        with open(json_info, 'r') as compatibility_file:
            info = json.load(compatibility_file)
        with open(json_results, 'r') as factory_file:
            results = json.load(factory_file)
    except OSError:
        abort(404)
    job_id = TestLog.query.filter_by(hardware_id=hardware,
                                     job_name=job_name).first().id
    rev_options = ['未评审', '通过', '不通过']
    test_names = []
    test_results = []
    for i in results:
        if i.get("run"):
            test_names.append(i.get("name"))
            test_results.append(i.get("status"))
            review_obj = TestReview.query.filter_by(
                menu_id=manu, hardware_id=hardware, taskid=taskid,
                job_id=job_id,  testcase_name=i.get("name")).first()
            if review_obj:
                i['review_result'] = review_obj.review_result
                i['review_comments'] = review_obj.review_comments

    if form.validate_on_submit():
        rev_res = request.form.getlist("rev_res")
        rev_comment = request.form.getlist("rev_comment")
        for i, test_name in enumerate(test_names):
            review_obj = TestReview.query.filter_by(
                menu_id=manu, hardware_id=hardware, taskid=taskid,
                job_id=job_id, testcase_name=test_name).first()
            if not review_obj:
                review_data = TestReview(
                    testcase_name=test_name,
                    review_result=rev_res[i],
                    review_comments=rev_comment[i],
                    create_time=datetime.datetime.now().strftime(
                        '%Y-%m-%d %H:%M:%S'),
                    test_result=test_results[i],
                    hardware_id=hardware,
                    menu_id=manu,
                    taskid=taskid,
                    job_id=job_id
                )
                db.session.add(review_data)
                db.session.commit()
            else:
                review_obj.review_result = rev_res[i]
                review_obj.review_comments = rev_comment[i]
                review_obj.create_time = datetime.datetime.now().strftime(
                    '%Y-%m-%d %H:%M:%S')
                db.session.commit()
        for i in results:
            if i.get("run"):
                review_obj = TestReview.query.filter_by(
                    menu_id=manu, hardware_id=hardware, taskid=taskid,
                    job_id=job_id, testcase_name=i.get("name")).first()
                if review_obj:
                    i['review_result'] = review_obj.review_result
                    i['review_comments'] = review_obj.review_comments
    return render_template('admin/job.html', manu=manu_name,
                           hardware=hardware_name, taskid=taskid,
                           job=job_name, info=info, results=results,
                           rev_options=rev_options, form=form)


@admin.route('/login/', methods=['GET', 'POST'])
def login():
    """用户登录"""
    form = LoginForm()
    if form.validate_on_submit():
        data = form.data
        # 判断验证码
        if session.get('image').lower() != form.verify_code.data.lower():
            flash('验证码错误', "err")
            return render_template("/admin/login.html", form=form)  # 返回登录页
        # 判断用户名是否存在
        user = Admin.query.filter_by(
            username=data["username"]).first()
        if not user:
            flash("用户名不存在", "err")
            return render_template("/admin/login.html", form=form)
        if not user.check_password(
                data["password"]):
            flash("密码错误！",  "err")
            return render_template("/admin/login.html", form=form)
        session["user_id"] = user.id
        session["user"] = user.username
        return redirect(url_for('admin.index'))
    return render_template('/admin/login.html', form=form)


@admin.route('/logout/', methods=['GET', 'POST'])
def logout():
    """用户登出"""
    session.pop('user', None)
    return redirect(url_for('admin.login'))


@admin.route('/api/job/upload', methods=['GET', 'POST'])
def save_upload_log():
    """上传的日志信息保存到数据库和指定的文件夹下"""
    host = request.values.get('host', '').strip()
    oec_id = request.values.get('id', '').strip().replace(' ', '-')
    job = request.values.get('job', '').strip().replace(' ', '-')
    filetext = request.values.get('filetext', '')
    host_lst = host.split(" ")
    manu = host_lst[0]
    hardware = host_lst[1] + '_' + host_lst[2] + '_' + host_lst[3]
    if not all([host, oec_id, job, filetext]):
        return render_template('upload.html', host=host, id=id, job=job,
                               filetext=filetext, ret='Failed'), 400
    dir_job = os.path.join(dir_results, manu, hardware, oec_id, job)
    tar_job = dir_job + '.tar.gz'
    if not os.path.exists(dir_job):
        os.makedirs(dir_job)
    try:
        with open(tar_job, 'wb') as job_file:
            job_file.write(base64.b64decode(filetext))
        os.system("tar xf '%s' -C '%s'" % (tar_job, os.path.dirname(dir_job)))

        if not Hardware.query.filter_by(hardware=hardware).count():
            hardware_data = Hardware(
                hardware=hardware,
                manu_id=Manufacturer.query.filter_by(
                    manufacturer_name=manu).first().id
            )
            db.session.add(hardware_data)
            db.session.commit()

        data = TestLog(
            task_id=oec_id,
            job_name=job,
            upload_time=datetime.datetime.now().strftime(
                '%Y-%m-%d %H:%M:%S'),
            hardware_id=Hardware.query.filter_by(hardware=hardware).first().id
        )
        db.session.add(data)
        db.session.commit()
    except (OSError, ValueError):
        abort(400)
    return render_template('upload.html', host=host, id=oec_id, job=job,
                           filetext=filetext, ret='Successful')


@admin.route("/delete/<string:hardware_id>/<string:task_id>/<string:job_name>",
             methods=["GET"])
@admin_login
def job_del(hardware_id=None, task_id=None, job_name=None):
    """
    删除job日志
    """
    hardware_name = Hardware.query.filter_by(id=hardware_id).first().hardware
    job = TestLog.query.filter_by(job_name=job_name).filter_by(
        hardware_id=hardware_id).first()
    reviews = TestReview.query.filter_by(job_id=int(job.id)).count()
    if reviews:
        return "<script>alert('该日志已被评审，不能被删除！');history.go(-1);</script>"
    db.session.delete(job)  # 删除数据
    db.session.commit()  # 提交数据
    manu_id = Hardware.query.get(int(hardware_id)).manu_id
    namu_name = Manufacturer.query.get(manu_id).manufacturer_name
    path = os.path.join(dir_results, namu_name, hardware_name, task_id,
                        job_name)
    tar_path = path + ".tar.gz"
    shutil.rmtree(path)
    os.remove(tar_path)
    return redirect(url_for('admin.get_logs', manu_id=manu_id,
                            hardware_id=hardware_id, taskid=task_id))
